home *** CD-ROM | disk | FTP | other *** search
- #ifdef REMOTE_PLAYER
- /* @(#)socket.c 1.4 91/11/13
- *
- * Various socket related routines used by reve for a networked game.
- * These routines are based on those found in the BSD talk program,
- * which is:
- *
- * Copyright (c) 1983 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that: (1) source distributions retain this entire copyright
- * notice and comment, and (2) distributions including binaries display
- * the following acknowledgement: ``This product includes software
- * developed by the University of California, Berkeley and its contributors''
- * in the documentation or other materials provided with the distribution
- * and in all advertising materials mentioning features or use of this
- * software. Neither the name of the University nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- #include "reve.h"
- #include <errno.h>
- #include "extern.h"
- #include <memory.h>
- #include <netdb.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include "ctl.h"
-
- #ifdef X11
- #include <X11/Xos.h>
- #endif /*X11*/
-
- extern int errno ;
-
- static CTL_MSG msg ;
-
- static struct sockaddr_in daemon_addr = { AF_INET } ;
- static struct sockaddr_in ctl_addr = { AF_INET } ;
- static struct sockaddr_in my_addr = { AF_INET } ;
-
- static struct in_addr lmachine_addr ; /* Inet address of this machine. */
- static struct in_addr rmachine_addr ; /* Inet address of remote machine. */
- static struct itimerval itimer ;
- static struct timeval wait = { MSG_INTERVAL, 0 } ;
-
- static u_short daemon_port ; /* Port number of the Reve daemon. */
-
- static char *current_state ;
-
- static int ctl_sockt ;
- static int invitation_waiting ;
-
- /* The msg.id's for the invitations on the local and remote machines.
- * These are used to delete the invitations.
- */
-
- static int local_id ;
- static int remote_id ;
-
- static jmp_buf invitebuf ;
-
- static CTL_RESPONSE swapresponse P((CTL_RESPONSE)) ;
-
- static int look_for_invite P((CTL_RESPONSE *)) ;
-
- static void announce_invite P(()) ;
- static void ctl_transact P((struct in_addr, CTL_MSG, int, CTL_RESPONSE *)) ;
- static void disp_message P(()) ;
- static void re_invite P(()) ;
- static void send_delete P(()) ;
-
-
- static void
- announce_invite() /* Transmit the invitation and process the response */
- {
- CTL_RESPONSE response ;
-
- current_state = "Trying to connect to your opponent's reve daemon" ;
-
- ctl_transact(rmachine_addr, msg, ANNOUNCE, &response) ;
- remote_id = response.id_num ;
-
- if (response.answer != SUCCESS)
- {
- switch (response.answer)
- {
- case NOT_HERE : message(PANEL_MES,
- "Your opponent is not logged on") ;
- break ;
-
- case MACHINE_UNKNOWN : message(PANEL_MES,
- "Remote machine does not recognize us") ;
- break ;
-
- case UNKNOWN_REQUEST : message(PANEL_MES,
- "Remote machine can not handle reve") ;
- break ;
-
- case FAILED : message(PANEL_MES,
- "Remote machine is too confused") ;
- break ;
-
- case PERMISSION_DENIED : message(PANEL_MES,
- "Your opponent is refusing messages") ;
- }
- if (invitation_waiting) send_delete() ;
- destroy_reve() ;
- }
-
- /* Leave the actual invitation on my reve daemon. */
-
- ctl_transact(lmachine_addr, msg, LEAVE_INVITE, &response) ;
- local_id = response.id_num ;
- }
-
-
- /* SOCKDGRAM is unreliable, so we must repeat messages if we have
- * not received an acknowledgement within a reasonable amount of time
- */
-
- static void
- ctl_transact(target, msg, type, response)
- struct in_addr target ;
- CTL_MSG msg ;
- int type ;
- CTL_RESPONSE *response ;
- {
- struct sockaddr junk ;
- struct timeval wait ;
- int cc, junk_size, nready ;
-
- #ifdef NO_43SELECT
- int ctl_mask, read_mask ;
- #else
- fd_set ctl_mask, read_mask ;
- #endif /*NO_43SELECT*/
-
- wait.tv_sec = CTL_WAIT ;
- wait.tv_usec = 0 ;
-
- msg.type = type ;
-
- daemon_addr.sin_addr = target ;
- daemon_addr.sin_port = daemon_port ;
-
- #ifdef NO_43SELECT
- ctl_mask = 1 << ctl_sockt ;
- #else
- FD_ZERO(&ctl_mask) ;
- FD_SET(ctl_sockt, &ctl_mask) ;
- #endif /*NO_43SELECT*/
-
- /* Keep sending the message until a response of the right type is obtained. */
-
- do
- {
- do
- {
- cc = sendto(ctl_sockt, (char *) &msg, sizeof(CTL_MSG), 0,
- &daemon_addr, sizeof(daemon_addr)) ;
-
- if (cc != sizeof(CTL_MSG))
- {
- if (errno == EINTR) continue ; /* Returning from an interrupt. */
- else perror("Error on write to reve daemon") ;
- }
-
- read_mask = ctl_mask ;
-
- #ifdef NO_43SELECT
- while ((nready = select(32, &read_mask, 0, 0, &wait)) < 0)
- #else
- while ((nready = select(FD_SETSIZE, &read_mask,
- (fd_set *) 0, (fd_set *) 0, &wait)) < 0)
- #endif /*NO_43SELECT*/
- {
- if (errno == EINTR) continue ; /* Returning from an interrupt. */
- else perror("Error on waiting for response from daemon") ;
- }
- }
- while (nready == 0) ;
-
- /* Keep reading while there are queued messages (this is not necessary,
- * it just saves extra request/acknowledgements being sent).
- */
-
- do
- {
- junk_size = sizeof(junk) ;
- cc = recvfrom(ctl_sockt, (char *) response,
- sizeof(CTL_RESPONSE), 0, &junk, &junk_size) ;
- if (cc < 0)
- {
- if (errno == EINTR) continue;
- perror("Error on read from reve daemon") ;
- }
-
- read_mask = ctl_mask ;
-
- /* An immediate poll. */
-
- timerclear(&wait) ;
- #ifdef NO_43SELECT
- nready = select(32, &read_mask, 0, 0, &wait) ;
- #else
- nready = select(FD_SETSIZE, &read_mask,
- (fd_set *) 0, (fd_set *) 0, &wait) ;
- #endif /*NO_43SELECT*/
- }
- while (nready > 0 && response->type != type) ;
- }
- while (response->type != type) ;
- }
-
-
- static SIGRET
- disp_msg()
- {
- message(PANEL_MES, current_state) ;
- }
-
-
- void
- end_msgs()
- {
- SIGNAL(SIGALRM, SIG_IGN) ;
- timerclear(&itimer.it_value) ;
- timerclear(&itimer.it_interval) ;
- setitimer(ITIMER_REAL, &itimer, (struct timerval *) 0) ;
- }
-
-
- void
- get_addrs(luserhost, ruserhost)
- char *luserhost, *ruserhost ;
- {
- struct hostent *hp ;
- struct servent *sp ;
- char *ptr, luser[MAXLINE], lhost[MAXLINE], ruser[MAXLINE], rhost[MAXLINE] ;
-
- ptr = index(luserhost, '@') ;
- STRNCPY(luser, luserhost, ptr - luserhost) ;
- luser[ptr - luserhost] = '\0' ;
- STRCPY(lhost, ptr+1) ;
-
- if ((ptr = index(ruserhost, '@')) == NULL)
- {
- STRCPY(ruser, ruserhost) ;
- STRCPY(rhost, lhost) ; /* No hostname; assume local host. */
- }
- else
- {
- STRNCPY(ruser, ruserhost, ptr - ruserhost) ;
- ruser[ptr - ruserhost] = '\0' ;
- STRCPY(rhost, ptr+1) ;
- }
-
- hp = gethostbyname(lhost) ; /* Look up address of local host. */
-
- if (hp == (struct hostent *) 0)
- {
- PRINTF("Local host (%s) doesn't exist.\n", lhost) ;
- exit(1) ;
- }
-
- if (hp->h_addrtype != AF_INET)
- {
- PRINTF("Protocal mix up with local machine address\n") ;
- exit(-1) ;
- }
-
- MEMCPY((char *) &lmachine_addr, hp->h_addr, hp->h_length) ;
-
- /* If s/he is on the same machine, then simply copy. */
-
- if (memcmp((char *) rhost, (char *) lhost, sizeof(lhost)) == 0)
- MEMCPY((char *) &rmachine_addr, (char *) &lmachine_addr,
- sizeof(rmachine_addr)) ;
- else
- {
- if ((rmachine_addr.s_addr = inet_addr(rhost)) == -1)
- {
-
- /* Look up the address of the recipient's machine. */
-
- if ((hp = gethostbyname(rhost)) == (struct hostent *) 0)
- {
- PRINTF("%s is an unknown host\n", rhost) ;
- exit(1) ;
- }
-
- if (hp->h_addrtype != AF_INET)
- {
- PRINTF("Protocol mix up with remote machine address\n") ;
- exit(1) ;
- }
- MEMCPY((char *) &rmachine_addr, hp->h_addr, hp->h_length) ;
- }
- }
-
- /* Find the daemon portal. */
-
- if ((sp = getservbyname("reve", "udp")) == NULL)
- {
- perror("This machine doesn't support a tcp reve daemon") ;
- exit(1) ;
- }
-
- if (strcmp(sp->s_proto, "udp") != 0)
- {
- PRINTF("Protocol mix up with reve daemon\n") ;
- exit(1) ;
- }
- daemon_port = sp->s_port ;
-
- /* Load these useful values into the standard message header */
-
- msg.id_num = 0 ;
- msg.dtype = dtype ;
-
- STRNCPY(msg.l_name, luser, NAME_SIZE) ;
- msg.l_name[NAME_SIZE - 1] = '\0' ;
-
- STRNCPY(msg.r_name, ruser, NAME_SIZE) ;
- msg.r_name[NAME_SIZE - 1] = '\0' ;
-
- STRNCPY(msg.r_tty, "", TTY_SIZE) ; /* Possibly allow tty name later. */
- msg.r_tty[TTY_SIZE - 1] = '\0' ;
- }
-
-
- /* There wasn't an invitation waiting, so send a request containing
- * our socket address to the remote reve daemon so it can invite them.
- */
-
- void
- invite_remote()
- {
- int nfd, read_mask, template, new_sockt ;
- struct itimerval itimer ;
- CTL_RESPONSE response ;
-
- itimer.it_value.tv_sec = RING_WAIT ;
- itimer.it_value.tv_usec = 0 ;
- itimer.it_interval = itimer.it_value ;
-
- if (listen(socketfd, 5) != 0)
- perror("Error on attempt to listen for caller") ;
-
- msg.addr = my_addr ;
- msg.id_num = -1 ; /* An impossible id_num. */
-
- invitation_waiting = 1 ;
-
- announce_invite() ;
-
- /* Shut off the automatic messages for a while,
- * so we can use the interupt timer to resend the invitation.
- */
-
- end_msgs() ;
- setitimer(ITIMER_REAL, &itimer, (struct itimerval *) 0) ;
- message(PANEL_MES, "Waiting for your opponent to respond") ;
- SIGNAL(SIGALRM, re_invite) ;
- SETJMP(invitebuf) ;
-
- while ((new_sockt = accept(socketfd, 0, 0)) < 0)
- {
- if (errno != EINTR)
- perror("Unable to connect with your opponent") ;
- else
- continue ; /* We just returned from a interupt, keep trying. */
- }
-
- CLOSE(socketfd) ;
- socketfd = new_sockt ;
-
- /* Have the daemons delete the invitations now that we have connected. */
-
- current_state = "Waiting for your opponent to respond" ;
- start_msgs() ;
-
- msg.id_num = local_id ;
- ctl_transact(lmachine_addr, msg, DELETE, &response) ;
- msg.id_num = remote_id ;
- ctl_transact(rmachine_addr, msg, DELETE, &response) ;
- invitation_waiting = 0 ;
- }
-
-
- int
- is_local() /* See if the local daemon has a invitation for us. */
- {
- CTL_RESPONSE response ;
-
- /* The rest of msg was set up in get_names. */
-
- msg.ctl_addr = ctl_addr ;
-
- if (!look_for_invite(&response))
- return(0) ; /* We must be initiating a connection. */
-
- /* There was an invitation waiting for us,
- * so connect with the other (hopefully waiting) party
- */
-
- current_state = "Waiting to connect with caller" ;
-
- response = swapresponse(response) ;
- while (connect(socketfd, &response.addr, sizeof(response.addr)) != 0)
- {
- if (errno == ECONNREFUSED)
- {
-
- /* The caller gave up, but his invitation somehow
- * was not cleared. Clear it and initiate an
- * invitation. (We know there are no newer invitations,
- * the reved works LIFO.)
- */
-
- ctl_transact(rmachine_addr, msg, DELETE, &response) ;
- CLOSE(socketfd) ;
- open_socket() ;
- return(0) ;
- }
- else if (errno == EINTR)
- continue ; /* We have returned from an interupt handler. */
- else
- perror("Unable to connect with initiator") ;
- }
- return(1) ;
- }
-
-
- static int
- look_for_invite(response) /* Look for an invitation on 'machine'. */
- CTL_RESPONSE *response ;
- {
- struct in_addr machine_addr ;
-
- current_state = "Checking for invitation on caller's machine" ;
-
- ctl_transact(rmachine_addr, msg, LOOK_UP, response) ;
-
- /* The switch is for possible later options, such as multiple invitations. */
-
- switch (response->answer)
- {
- case SUCCESS : msg.id_num = response->id_num ;
- dtype = (response->dtype == XBLACK) ? XWHITE : XBLACK ;
- return(1) ;
-
- default : /* There wasn't an invitation waiting for us. */
- return(0) ;
- }
- }
-
-
- void
- open_ctl() /* Open the ctl socket. */
- {
- int length ;
-
- ctl_addr.sin_port = 0 ;
- ctl_addr.sin_addr = lmachine_addr ;
-
- if ((ctl_sockt = socket(AF_INET, SOCK_DGRAM, 0, 0)) <= 0)
- perror("Bad socket") ;
-
- if (bind(ctl_sockt, &ctl_addr, sizeof(ctl_addr), 0) != 0)
- perror("Couldn't bind to control socket") ;
-
- length = sizeof(ctl_addr) ;
- if (getsockname(ctl_sockt, &ctl_addr, &length) == -1)
- perror("Bad address for ctl socket") ;
-
- current_state = "No connection yet" ;
- }
-
-
- void
- open_socket()
- {
- int length ;
-
- my_addr.sin_addr = lmachine_addr ;
- my_addr.sin_port = 0 ;
-
- if ((socketfd = socket(AF_INET, SOCK_STREAM, 0, 0)) <= 0)
- perror("Bad socket") ;
-
- if (bind(socketfd, &my_addr, sizeof(my_addr)) != 0)
- perror("Binding local socket") ;
-
- length = sizeof(my_addr);
- if (getsockname(socketfd, &my_addr, &length) == -1)
- perror("Bad address for socket") ;
- }
-
-
- void
- read_from_sock(socketfd) /* Read move from remote user@host. */
- int socketfd ;
- {
- struct reve_out out ;
- int sout ;
-
- sout = sizeof(struct reve_out) ;
- if (read(socketfd, (char *) &out, sout) <= 0)
- {
- FPRINTF(stderr, "%s: Connection to opponent closed. Exiting.\n",
- progname) ;
- destroy_reve() ;
- }
- else
- {
- if (out.type == M_MOVE)
- {
- set_cursor(CANVASCUR) ;
- move = out.move ;
- note = out.note ;
- opponent_move(next_player) ;
- }
- }
- }
-
-
- static void
- re_invite() /* Routine called on interupt to re-invite the callee. */
- {
- message(PANEL_MES, "Ringing your opponent again") ;
- msg.id_num = remote_id + 1 ; /* Force a re-announce. */
- announce_invite() ;
- longjmp(invitebuf, 1) ;
- }
-
-
- static void
- send_delete() /* Tell the daemon to remove your invitation. */
- {
- msg.type = DELETE;
-
- /* This is just a extra clean up, so just send it and don't wait
- * for an answer.
- */
-
- msg.id_num = remote_id ;
- daemon_addr.sin_addr = rmachine_addr ;
- if (sendto(ctl_sockt, &msg, sizeof(CTL_MSG), 0, &daemon_addr,
- sizeof(daemon_addr)) != sizeof(CTL_MSG))
- perror("send_delete remote") ;
-
- msg.id_num = local_id ;
- daemon_addr.sin_addr = lmachine_addr ;
- if (sendto(ctl_sockt, &msg, sizeof(CTL_MSG), 0, &daemon_addr,
- sizeof(daemon_addr)) != sizeof(CTL_MSG))
- perror("send_delete local") ;
- }
-
-
- void
- start_msgs()
- {
- message(PANEL_MES, current_state) ;
- SIGNAL(SIGALRM, disp_msg) ;
- itimer.it_value = itimer.it_interval = wait ;
- setitimer(ITIMER_REAL, &itimer, (struct timerval *) 0) ;
- }
-
-
- /* Heuristic to detect if need to reshuffle CTL_RESPONSE structure. */
-
- #if defined(mc68000) || defined(hpux)
- struct ctl_response_runrise {
- char type ;
- char answer ;
- short junk ;
- int id_num ;
- struct sockaddr_in addr ;
- } ;
-
-
- static CTL_RESPONSE
- swapresponse(rsp)
- CTL_RESPONSE rsp ;
- {
- struct ctl_response_runrise swaprsp ;
-
- if (rsp.addr.sin_family != AF_INET)
- {
- MEMCPY(&swaprsp, &rsp, sizeof(CTL_RESPONSE)) ;
- if (swaprsp.addr.sin_family == AF_INET)
- {
- rsp.addr = swaprsp.addr ;
- rsp.type = swaprsp.type ;
- rsp.answer = swaprsp.answer ;
- rsp.id_num = swaprsp.id_num ;
- }
- }
- return(rsp) ;
- }
- #endif
-
-
- #ifdef sparc
- struct ctl_response_sun3 {
- char type ;
- char answer ;
- unsigned short id_num2 ;
- unsigned short id_num1 ;
- short sin_family ;
- short sin_port ;
- short sin_addr2 ;
- short sin_addr1 ;
- } ;
-
-
- static CTL_RESPONSE
- swapresponse(rsp)
- CTL_RESPONSE rsp ;
- {
- struct ctl_response_sun3 swaprsp ;
-
- if (rsp.addr.sin_family != AF_INET)
- {
- MEMCPY(&swaprsp, &rsp, sizeof(struct ctl_response_sun3)) ;
- if (swaprsp.sin_family == AF_INET)
- {
- rsp.type = swaprsp.type ;
- rsp.answer = swaprsp.answer ;
- rsp.id_num = swaprsp.id_num1 | (swaprsp.id_num2 << 16) ;
- rsp.addr.sin_family = swaprsp.sin_family ;
- rsp.addr.sin_port = swaprsp.sin_port ;
- rsp.addr.sin_addr.s_addr = (swaprsp.sin_addr2 << 16) |
- swaprsp.sin_addr1 ;
- }
- }
- return(rsp) ;
- }
- #endif
-
-
- void
- write_to_sock(socketfd, move) /* Write move to remote user@host. */
- int socketfd, move ;
- {
- struct reve_out out ;
-
- out.type = M_MOVE ;
- out.move = move ;
- out.note = 0 ;
- out.depth = 0 ;
- processing = TRUE ;
- WRITE(socketfd, (char *) &out, sizeof(struct reve_out)) ;
- }
- #endif /* REMOTE_PLAYER */
-